Android AsyncTask的骗术
Why
首先,说一下标题为什么说是骗术
? Google提供的AsyncTask,我一直认为是一个使用线程池来处理任务的并发的高性能类,淡然这句话猛一看没有什么问题。但是问题出现的我的使用上,如果你使用它时默认也是调用的execute(Params… params)这个方法,那我感觉我应该善意的提醒你“哥们你被骗了”,其实这个方法内部的执行不是并发处理的,而是串行处理的。下面我们来看一下源码。
源码分析
1 |
|
该方法直接支行了executeOnExecutor方法,猛一看是没有什么问题。然而仔细看我们就需要研究一下sDefaultExecutor
这个东西。1
2
3
4
5
6/**
* An {@link Executor} that executes tasks one at a time in serial
* order. This serialization is global to a particular process.
*/
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
细心的朋友们已经看到了,从SERIAL_EXECUTOR
的注视上看到了executes tasks one at a time in serial order(按照序列执行任务)
。好了,如果还不死心,就只能去看SerialExecutor
源码了。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
其实这个SerialExecutor是AsyncTask的内部类,从上边源码会发现sDefaultExecutor实际上是一个串行的线程池。先把任务插入到mTasks的队列中,没有活动的再去调用下一个。从这一点可以看出,再默认情况下AsyncTask是串行执行的。
#验证:
1 |
|
看一下代码打印的log1
2
3
4
5
6
7
8
9
10
11
1201-07 17:06:54.628 D/AsyncTaskActivity: **********execute串行执行方法**********
01-07 17:06:55.630 D/AsyncTaskActivity: finish at 2016-01-07 17:06:55
01-07 17:06:56.635 D/AsyncTaskActivity: finish at 2016-01-07 17:06:56
01-07 17:06:57.636 D/AsyncTaskActivity: finish at 2016-01-07 17:06:57
01-07 17:06:58.638 D/AsyncTaskActivity: finish at 2016-01-07 17:06:58
01-07 17:06:59.638 D/AsyncTaskActivity: finish at 2016-01-07 17:06:59
01-07 17:07:01.980 D/AsyncTaskActivity: **********executeOnExcutor并发执行方法**********
01-07 17:07:02.982 D/AsyncTaskActivity: finish at 2016-01-07 17:07:02
01-07 17:07:02.982 D/AsyncTaskActivity: finish at 2016-01-07 17:07:02
01-07 17:07:02.982 D/AsyncTaskActivity: finish at 2016-01-07 17:07:02
01-07 17:07:02.982 D/AsyncTaskActivity: finish at 2016-01-07 17:07:02
01-07 17:07:02.982 D/AsyncTaskActivity: finish at 2016-01-07 17:07:02
从Log日志上可以看到execute方法是串行执行的,executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,””)是并行执行的。
How should we do ?
既然AsyncTask默认是串行执行的,那么我如果想要使用并行执行任务,应该怎么办呢?其实也很简单,AsyncTask已经给我们提供了,只需要简单调用就OK了。executeOnExecutor(Executor exec, Params.. params)
该方法也是public的,因此我么直接调用它,给他传递一个Executor就OK了,这里AsyncTask默认也给我们设定了一个并行的Executor叫做THREAD_POOL_EXECUTOR
。因此,我们只需要 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26`注意:executeOnExecutor这个方法是API 11以上才有的`
```java
/** An {@link Executor} that can be used to execute tasks in parallel. */
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}